#pragma once

#include "device.hpp"
#include "json.hpp"
#include <any>

/* msg to json object */
/* json object to msg */

using json = nlohmann::json;

namespace Protocol {
struct Msg_t {
    std::size_t tick;
    std::size_t time;
    std::string src;

    Msg_t() = default;
    ~Msg_t() = default;
    Msg_t(std::size_t t, std::size_t ti, const std::string &s)
        : tick(t)
        , time(ti)
        , src(s)
    {
    }
};

struct AckMsg_t : public Msg_t {
    struct {
        std::size_t tick;
        std::size_t time;
    } ack;
};

struct ReadIdMsg_t : public Msg_t {
    struct point {
        std::string key;

        friend void from_json(const json &j, point &p)
        {
            if (j.contains("key")) {
                j.at("key").get_to(p.key);
            }
        }
    };

    struct device {
        std::size_t id;
        std::vector<point> points;

        friend void from_json(const json &j, device &d)
        {
            if (j.contains("id")) {
                j.at("id").get_to(d.id);
            }

            if (j.contains("points")) {
                if (j.at("points").is_array()) {
                    for (const auto &item : j.at("points")) {
                        point p;
                        item.get_to(p);
                        d.points.push_back(p);
                    }
                }
            }
        }
    };

    struct Dev_t {
        std::vector<device> dev;

        friend void from_json(const json &j, Dev_t &d)
        {
            if (j.contains("devices")) {
                if (j.at("devices").is_array()) {
                    for (const auto &item : j.at("devices")) {
                        device dev;
                        item.get_to(dev);
                        d.dev.emplace_back(dev);
                    }
                }
            }
        }
    };

    Dev_t dev;

    friend void from_json(const json &j, ReadIdMsg_t &r)
    {
        if (j.contains("tick")) {
            j.at("tick").get_to(r.tick);
        }

        if (j.contains("time")) {
            j.at("time").get_to(r.time);
        }

        if (j.contains("src")) {
            j.at("src").get_to(r.src);
        }

        if (j.contains("dev")) {
            j.at("dev").get_to(r.dev);
        }
    }
};

struct ReadIdMsgAck_t : public AckMsg_t {
    struct point {
        std::string key;
        std::any val;
        std::size_t time;
        std::string status;

        point &setStatus(bool state)
        {
            status = state ? "success" : "failed";
            return *this;
        }

        template <typename T>
        point &setKeyValue(const T &&value)
        {
            val = std::make_any<T>(value);
            return *this;
        }
    };

    struct device {
        std::size_t id;
        std::string status;
        std::vector<point> points;

        device &setStatus(bool state)
        {
            status = state ? "online" : "offline";
            return *this;
        }
    };

    struct Dev {
        std::vector<device> devices;
    };

    Dev _dev;

    ReadIdMsgAck_t(const ReadIdMsg_t &r)
    {
        tick = r.tick;
        time = r.time;

        ack.tick = r.tick;
        ack.time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();

        for (const auto &dev : r.dev.dev) {
            device d;
            d.id = dev.id;
            for (const auto &p : dev.points) {
                point _p;
                _p.key = p.key;
                _p.time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
                _p.val = 0;
                d.points.emplace_back(_p);
            }
            _dev.devices.emplace_back(d);
        }
    }

    template <typename T>
    ReadIdMsgAck_t &setKeyValue(device &d, const std::string &key, const T &&value)
    {
        for (auto &point : d.points) {
            if (point.key == key) {
                point.val = std::make_any<T>(value);
            }
        }

        return *this;
    }

    ReadIdMsgAck_t &setSrc(const std::string &s)
    {
        src = s;
        return *this;
    }

    ReadIdMsgAck_t &setStatus(device &d, bool isOnline)
    {
        d.status = isOnline ? "online" : "offline";
        return *this;
    }

    friend void to_json(json &j, const ReadIdMsgAck_t &r)
    {
        j["tick"] = r.tick;
        j["time"] = r.time;
        j["src"] = r.src;
        j["ack"]["tick"] = r.ack.tick;
        j["ack"]["time"] = r.ack.time;

        for (const auto &dev : r._dev.devices) {
            json temp;
            temp["id"] = dev.id;
            temp["status"] = dev.status;

            for (const auto &point : dev.points) {
                json p;
                p["key"] = point.key;
                p["time"] = point.time;

                if (point.val.type() == typeid(int)) {
                    p["val"] = std::any_cast<int>(point.val);
                } else if (point.val.type() == typeid(float)) {
                    p["val"] = std::any_cast<float>(point.val);
                } else if (point.val.type() == typeid(double)) {
                    p["val"] = std::any_cast<double>(point.val);
                } else if (point.val.type() == typeid(uint32_t)) {
                    p["val"] = std::any_cast<uint32_t>(point.val);
                } else if (point.val.type() == typeid(std::string)) {
                    p["val"] = std::any_cast<std::string>(point.val);
                }
                p["status"] = point.status;

                temp["points"].push_back(p);
            }

            j["dev"]["devices"].push_back(temp);
        }
    }
};

struct ReadModelMsg_t : public Msg_t {
    struct point {
        std::string key;

        friend void from_json(const json &j, point &p)
        {
            if (j.contains("key")) {
                j.at("key").get_to(p.key);
            }
        }
    };

    struct device {
        std::string model;
        std::vector<point> points;

        friend void from_json(const json &j, device &d)
        {
            if (j.contains("model")) {
                j.at("model").get_to(d.model);
            }

            if (j.contains("points")) {
                if (j.at("points").is_array()) {
                    for (const auto &item : j.at("points")) {
                        point p;
                        item.get_to(p);
                        d.points.emplace_back(p);
                    }
                }
            }
        }
    };

    struct Dev {
        std::vector<device> devices;

        friend void from_json(const json &j, Dev &d)
        {
            if (j.contains("devices")) {
                if (j.at("devices").is_array()) {
                    for (const auto &item : j.at("devices")) {
                        device dev;
                        item.get_to(dev);
                        d.devices.emplace_back(dev);
                    }
                }
            }
        }
    };

    Dev _dev;

    friend void from_json(const json &j, ReadModelMsg_t &r)
    {
        if (j.contains("tick")) {
            j.at("tick").get_to(r.tick);
        }

        if (j.contains("time")) {
            j.at("time").get_to(r.time);
        }

        if (j.contains("src")) {
            j.at("src").get_to(r.src);
        }

        if (j.contains("dev")) {
            j.at("dev").get_to(r._dev);
        }
    }

    friend std::ostream &operator<<(std::ostream &out, const ReadModelMsg_t &r)
    {
#define TABLE "  "
        out << "{\n";
        out << TABLE << "src --> " << r.src << std::endl;
        out << TABLE << "tick --> " << r.tick << std::endl;
        out << TABLE << "time --> " << r.time << std::endl;
        out << TABLE << "model --> " << r._dev.devices.front().model << std::endl;
        out << TABLE << "key --> " << r._dev.devices.front().points.front().key << std::endl;
        out << "}";

        return out;
    }
};

struct Broadcast0Info_t : public Msg_t {
    struct Point {
        std::string key;
        std::any val;
    };

    struct Device {
        std::size_t id;
        std::string status;
        std::vector<Point> points;
    };

    struct Dev {
        std::vector<Device> devices;
    };

    Dev _dev;

    Broadcast0Info_t()
    {
        this->tick = 1;
        Point point {};
        Device dev {0, "offline", {point}};
        _dev.devices.push_back(dev);
    }

    Broadcast0Info_t(const std::string &src, std::size_t id, bool isOnline)
    {
        this->tick = 1;
        this->src = src;

        Point point {};

        /* 默认 (0/info)只上报一个设备的一个点 */
        Device dev {id, isOnline ? "online" : "offline", {point}};
        _dev.devices.push_back(dev);
    }

    template <typename T>
    Broadcast0Info_t &setKeyValue(const std::string &key, const T &value)
    {
        _dev.devices.front().points.front().key = key;
        _dev.devices.front().points.front().val = value;
        return *this;
    }

    friend void to_json(json &j, const Broadcast0Info_t &b)
    {
        j["tick"] = b.tick;
        j["time"] = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
        j["src"] = b.src;

        for (const auto &dev : b._dev.devices) {
            json temp;
            temp["id"] = dev.id;
            temp["status"] = dev.status;

            for (const auto &p : dev.points) {
                json point;
                point["key"] = p.key;

                if (p.val.type() == typeid(int)) {
                    point["val"] = std::any_cast<int>(p.val);
                } else if (p.val.type() == typeid(float)) {
                    point["val"] = std::any_cast<float>(p.val);
                } else if (p.val.type() == typeid(double)) {
                    point["val"] = std::any_cast<double>(p.val);
                } else if (p.val.type() == typeid(std::string)) {
                    point["val"] = std::any_cast<std::string>(p.val);
                } else if (p.val.type() == typeid(uint32_t)) {
                    point["val"] = std::any_cast<uint32_t>(p.val);
                }

                point["time"] = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();

                temp["points"].push_back(point);
            }

            j["dev"]["devices"].push_back(temp);
        }
    }
};

struct WriteIdMsg_t : public Msg_t {
    struct Point {
        std::string key;
        std::uint32_t val;

        friend void from_json(const json &j, Point &p)
        {
            if (j.contains("key")) {
                j.at("key").get_to(p.key);
            }

            if (j.contains("val")) {
                j.at("val").get_to(p.val);
            }
        }
    };

    struct Device {
        std::size_t id;
        std::vector<Point> points;

        friend void from_json(const json &j, Device &d)
        {
            if (j.contains("id")) {
                j.at("id").get_to(d.id);
            }

            if (j.contains("points")) {
                if (j.at("points").is_array()) {
                    for (const auto &item : j.at("points")) {
                        Point p;
                        item.get_to(p);
                        d.points.emplace_back(p);
                    }
                }
            }
        }
    };

    struct Dev {
        std::vector<Device> devices;

        friend void from_json(const json &j, Dev &d)
        {
            if (j.contains("devices")) {
                if (j.at("devices").is_array()) {
                    for (const auto &item : j.at("devices")) {
                        Device dev;
                        item.get_to(dev);
                        d.devices.push_back(dev);
                    }
                }
            }
        }
    };

    Dev _dev;

    friend void from_json(const json &j, WriteIdMsg_t &w)
    {
        if (j.contains("tick")) {
            j.at("tick").get_to(w.tick);
        }

        if (j.contains("time")) {
            j.at("time").get_to(w.time);
        }

        if (j.contains("src")) {
            j.at("src").get_to(w.src);
        }

        if (j.contains("dev")) {
            j.at("dev").get_to(w._dev);
        }
    }
};

struct WriteIdMsgAck_t : public AckMsg_t {
    struct Point {
        std::string key;
        std::uint32_t val;
        std::string status;

        friend void to_json(json &j, const Point &p)
        {
            j = {{"key", p.key}, {"val", p.val}, {"time", std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()}, {"status", p.status}};
        }

        Point &setStatus(bool state)
        {
            status = state ? "success" : "failed";
            return *this;
        }
    };

    struct Device {
        std::size_t id;
        std::string status;
        std::vector<Point> points;

        friend void to_json(json &j, const Device &d)
        {
            j["id"] = d.id;
            j["status"] = d.status;

            for (const auto &point : d.points) {
                json temp;
                to_json(temp, point);
                j["points"].push_back(temp);
            }
        }

        Device &setStatus(bool state)
        {
            status = state ? "online" : "offline";
            return *this;
        }
    };

    struct Dev {
        std::vector<Device> devices;

        friend void to_json(json &j, const Dev &d)
        {
            for (const auto &dev : d.devices) {
                json temp;
                to_json(temp, dev);
                j["devices"].push_back(temp);
            }
        }
    };

    Dev _dev;

    WriteIdMsgAck_t(const WriteIdMsg_t &w)
    {
        tick = w.tick;
        ack.tick = w.tick;

        for (const auto &dev : w._dev.devices) {
            Device device;
            device.id = dev.id;

            for (const auto &point : dev.points) {
                Point p;
                p.key = point.key;
                p.val = point.val;
                device.points.emplace_back(p);
            }

            _dev.devices.emplace_back(device);
        }
    }

    WriteIdMsgAck_t &setSrc(const std::string &src)
    {
        this->src = src;
        return *this;
    }

    WriteIdMsgAck_t &setStatus(Device &d, bool result)
    {
        d.status = result ? "online" : "offline";
        return *this;
    }

    friend void to_json(json &j, const WriteIdMsgAck_t &w)
    {
        j["tick"] = w.tick;
        j["time"] = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
        j["src"] = w.src;
        j["ack"]["tick"] = w.ack.tick;
        j["ack"]["time"] = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();

        json temp;
        to_json(temp, w._dev);

        j["dev"] = temp;
    }
};

struct RlistMsg_t : public Msg_t {
    std::size_t offset;
    std::size_t count;
    std::string type;

    RlistMsg_t() = default;
    RlistMsg_t(const std::string &src, const std::string &type, std::size_t offset, std::size_t count, std::size_t tick = 1, std::size_t time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
    {
        this->src = src;
        this->type = type;
        this->offset = offset;
        this->count = count;
        this->tick = tick;
        this->time = time;
    }

    friend void to_json(json &j, const RlistMsg_t &r)
    {
        j["tick"] = r.tick;
        j["time"] = r.time;
        j["src"] = r.src;
        j["dev"]["list"]["offset"] = r.offset;
        j["dev"]["list"]["count"] = r.count;
        j["dev"]["list"]["type"] = r.type;
    }
};

struct Param_t {
    std::size_t addr;
    std::size_t baudrate;
    std::size_t databits;
    std::size_t stopbits;
    std::string parity;
    std::string port;

    friend void from_json(const json &j, Param_t &p)
    {
        if (j.contains("address")) {
            j.at("address").get_to(p.addr);
        }

        if (j.contains("baudrate")) {
            j.at("baudrate").get_to(p.baudrate);
        }

        if (j.contains("bytesize")) {
            j.at("bytesize").get_to(p.databits);
        }

        if (j.contains("stopbits")) {
            j.at("stopbits").get_to(p.stopbits);
        }

        if (j.contains("port")) {
            j.at("port").get_to(p.port);
        }

        if (j.contains("parity")) {
            j.at("parity").get_to(p.parity);
        }
    }

    friend void to_json(json &j, const Param_t &p)
    {
        j = {{"address", p.addr}, {"baudrate", p.baudrate}, {"bytesize", p.databits}, {"stopbits", p.stopbits}, {"parity", p.parity}, {"port", p.port}};
    }
};

struct Device_t {
    std::string name;
    std::string uuid;
    std::size_t id;
    std::string type;
    std::string model;
    std::string profile;
    Param_t param;

    std::optional<std::string> status;
    std::optional<std::string> log;

    operator ModbusDevice_t()
    {
        ModbusDevice_t dev;
        dev.id = id;
        dev.name = name;
        dev.uuid = uuid;
        dev.hardware.baudrate = param.baudrate;
        dev.hardware.databits = param.databits;
        dev.hardware.parity = param.parity.length() ? param.parity[0] : 'e';
        dev.hardware.port = param.port;
        dev.slaveAddr = param.addr;
        dev.hardware.stopbits = param.stopbits;
        dev.profile = profile;
        dev.type = type;
        dev.model = model;

        dev.fromYaml();

        // auto ret = dev.pointTable.findPoint("DInfo");
        // std::cout << "find point ? " << std::boolalpha << ret.has_value() << std::endl;

        // std::cout << "point --> " << *ret.value() << std::endl;

        // std::cout << dev.pointTable << std::endl;

        return dev;
    }

    Device_t &setStatus(bool status)
    {
        this->status = status ? "success" : "failed";
        return *this;
    }

    Device_t &setLog(const std::string &errMsg)
    {
        this->log = errMsg;
        return *this;
    }

    friend void from_json(const json &j, Device_t &d)
    {
        if (j.contains("name")) {
            j.at("name").get_to(d.name);
        }

        if (j.contains("uuid")) {
            j.at("uuid").get_to(d.uuid);
        }

        if (j.contains("id")) {
            j.at("id").get_to(d.id);
        }

        if (j.contains("type")) {
            j.at("type").get_to(d.type);
        }

        if (j.contains("model")) {
            j.at("model").get_to(d.model);
        }

        if (j.contains("profile")) {
            j.at("profile").get_to(d.profile);
        }

        if (j.contains("param")) {
            j.at("param").get_to(d.param);
        }
    }

    friend void to_json(json &j, const Device_t &d)
    {
        j["name"] = d.name;
        j["id"] = d.id;
        j["uuid"] = d.uuid;
        j["type"] = d.type;
        j["model"] = d.model;
        j["profile"] = d.profile;

        if (d.status.has_value()) {
            j["status"] = d.status.value();
        }

        if (d.log.has_value()) {
            j["log"] = d.log.value();
        }

        json temp;
        to_json(temp, d.param);
        j["param"] = temp;
    }
};

struct List_t {
    std::size_t offset;
    std::size_t count;
    std::string type;
    std::size_t maxCount;

    friend void from_json(const json &j, List_t &l)
    {
        if (j.contains("offset")) {
            j.at("offset").get_to(l.offset);
        }

        if (j.contains("count")) {
            j.at("count").get_to(l.count);
        }

        if (j.contains("type")) {
            j.at("type").get_to(l.type);
        }

        if (j.contains("count_max")) {
            j.at("count_max").get_to(l.maxCount);
        }
    }
};

struct Dev_t {
    List_t list;
    std::vector<Device_t> devices;

    friend void from_json(const json &j, Dev_t &d)
    {
        if (j.contains("list")) {
            j.at("list").get_to(d.list);
        }

        if (j.contains("devices")) {
            if (j.at("devices").is_array()) {
                for (const auto &item : j.at("devices")) {
                    Device_t dev;
                    item.get_to(dev);
                    d.devices.push_back(dev);
                }
            }
        }
    }
};

struct RlistAckMsg_t : public AckMsg_t {
    Dev_t dev;

    friend void from_json(const json &j, RlistAckMsg_t &r)
    {
        if (j.contains("tick")) {
            j.at("tick").get_to(r.tick);
        }

        if (j.contains("time")) {
            j.at("time").get_to(r.time);
        }

        if (j.contains("src")) {
            j.at("src").get_to(r.src);
        }

        if (j.contains("ack")) {
            if (j.at("ack").contains("tick")) {
                j.at("ack").at("tick").get_to(r.ack.tick);
            }

            if (j.at("ack").contains("time")) {
                j.at("ack").at("time").get_to(r.ack.time);
            }
        }

        if (j.contains("dev")) {
            j.at("dev").get_to(r.dev);
        }
    }
};

struct AddMsg_t : public Msg_t {
    struct TempList {
        std::size_t count;

        friend void from_json(const json &j, TempList &l)
        {
            if (j.contains("count")) {
                j.at("count").get_to(l.count);
            }
        }
    };

    struct Devices {
        TempList list;
        std::vector<Device_t> devices;

        friend void from_json(const json &j, Devices &d)
        {
            if (j.contains("tmp_list")) {
                j.at("tmp_list").get_to(d.list);
            }

            if (j.contains("tmp_devices")) {
                if (j.at("tmp_devices").is_array()) {
                    for (const auto &item : j.at("tmp_devices")) {
                        Device_t dev;
                        item.get_to(dev);
                        d.devices.emplace_back(dev);
                    }
                }
            }
        }
    };

    Devices _dev;

    friend void from_json(const json &j, AddMsg_t &m)
    {
        if (j.contains("tick")) {
            j.at("tick").get_to(m.tick);
        }

        if (j.contains("time")) {
            j.at("time").get_to(m.time);
        }

        if (j.contains("src")) {
            j.at("src").get_to(m.src);
        }

        if (j.contains("dev")) {
            j.at("dev").get_to(m._dev);
        }
    }
};

struct AddMsgAck_t : public AckMsg_t {
    struct TempList {
        std::size_t count;

        friend void to_json(json &j, const TempList &t)
        {
            j["count"] = t.count;
        }
    };

    struct Devices {
        TempList list;
        std::vector<Device_t> devices;

        friend void to_json(json &j, const Devices &d)
        {
            {
                json temp;
                to_json(temp, d.list);
                j["tmp_list"] = temp;
            }

            for (const auto &dev : d.devices) {
                json temp;
                to_json(temp, dev);
                j["tmp_devices"].push_back(temp);
            }
        }
    };

    Devices _dev;

    AddMsgAck_t() = default;

    AddMsgAck_t(const AddMsg_t &a) /* 拷贝构造 */
    {
        tick = a.tick;
        time = a.time;
        ack.tick = a.tick;
        ack.time = a.time;

        _dev.list.count = a._dev.list.count;

        for (const auto &dev : a._dev.devices) {
            _dev.devices.push_back(dev);
        }
    }

    AddMsgAck_t &setSrc(const std::string &src)
    {
        this->src = src;
        return *this;
    }

    friend void to_json(json &j, const AddMsgAck_t &a)
    {
        j["tick"] = a.tick;
        j["time"] = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
        j["src"] = a.src;

        j["ack"]["tick"] = a.ack.tick;
        j["ack"]["time"] = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();

        json temp;
        to_json(temp, a._dev);
        j["dev"] = temp;
    }
};

struct DeleteMsg_t : public Msg_t {
    struct TempList {
        std::size_t count;

        friend void from_json(const json &j, TempList &t)
        {
            if (j.contains("count")) {
                j.at("count").get_to(t.count);
            }
        }
    };

    struct Dev {
        TempList list;
        std::vector<Device_t> devices;

        friend void from_json(const json &j, Dev &d)
        {
            if (j.contains("tmp_list")) {
                j.at("tmp_list").get_to(d.list);
            }

            if (j.contains("tmp_devices")) {
                if (j.at("tmp_devices").is_array()) {
                    for (const auto &item : j.at("tmp_devices")) {
                        Device_t dev;
                        item.get_to(dev);
                        d.devices.emplace_back(dev);
                    }
                }
            }
        }
    };

    Dev _dev;

    friend void from_json(const json &j, DeleteMsg_t &d)
    {
        if (j.contains("tick")) {
            j.at("tick").get_to(d.tick);
        }

        if (j.contains("time")) {
            j.at("time").get_to(d.time);
        }

        if (j.contains("src")) {
            j.at("src").get_to(d.src);
        }

        if (j.contains("dev")) {
            j.at("dev").get_to(d._dev);
        }
    }
};

struct DeleteMsgAck_t : public AckMsg_t {
    struct TempList {
        std::size_t count;

        friend void to_json(json &j, const TempList &t)
        {
            j["count"] = t.count;
        }
    };

    struct Devices {
        TempList list;
        std::vector<Device_t> devices;

        friend void to_json(json &j, const Devices &d)
        {
            {
                json temp;
                to_json(temp, d.list);
                j["tmp_list"] = temp;
            }

            for (const auto &dev : d.devices) {
                json temp;
                to_json(temp, dev);
                j["tmp_devices"].push_back(temp);
            }
        }
    };

    Devices _dev;

    DeleteMsgAck_t(const DeleteMsg_t &d)
    {
        tick = d.tick;
        ack.tick = d.tick;
        _dev.list.count = d._dev.list.count;
        _dev.devices = d._dev.devices;
    }

    DeleteMsgAck_t &setSrc(const std::string &s)
    {
        src = s;
        return *this;
    }

    friend void to_json(json &j, const DeleteMsgAck_t &d)
    {
        j["tick"] = d.tick;
        j["time"] = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
        j["src"] = d.src;

        j["ack"]["tick"] = d.tick;
        j["ack"]["time"] = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();

        json temp;
        to_json(temp, d._dev);
        j["dev"] = temp;
    }
};

struct AutoAddMsg_t : public Msg_t {
    struct Dev {
        std::string type;

        std::vector<HardWare_t> param;

        friend void from_json(const json &j, Dev &d)
        {
            if (j.contains("type")) {
                j.at("type").get_to(d.type);
            }

            if (j.contains("param")) {
                if (j.at("param").is_array()) {
                    for (auto &item : j.at("param")) {
                        HardWare_t h {};
                        item.get_to(h);
                        d.param.emplace_back(h);
                    }
                }
            }
        }
    };

    Dev dev;

    friend void from_json(const json &j, AutoAddMsg_t &a)
    {
        if (j.contains("tick")) {
            j.at("tick").get_to(a.tick);
        }

        if (j.contains("time")) {
            j.at("time").get_to(a.time);
        }

        if (j.contains("src")) {
            j.at("src").get_to(a.src);
        }

        if (j.contains("dev")) {
            j.at("dev").get_to(a.dev);
        }
    }
};

struct AutoFindAck_t : public AckMsg_t {
    struct TempList {
        std::size_t count;

        friend void to_json(json &j, const TempList &t)
        {
            j["count"] = t.count;
        }
    };

    struct TempDevice {
        std::string name;
        std::string uuid;
        std::string type;
        std::string model;
        Param_t param;

        TempDevice(const std::string &sn, const std::string &m, const HardWare_t &h, uint8_t addr)
        {
            name = sn;
            uuid = sn;
            type = "Modbus_RTU";
            model = m;
            param.addr = addr;
            param.baudrate = h.baudrate;
            param.databits = h.databits;
            param.parity = h.parity;
            param.port = h.port;
            param.stopbits = h.stopbits;
        }

        friend void to_json(json &j, const TempDevice &t)
        {
            j["name"] = t.name;
            j["uuid"] = t.uuid;
            j["type"] = t.type;
            j["model"] = t.model;

            json temp;
            to_json(temp, t.param);
            j["param"] = temp;
        }
    };

    struct Dev {
        TempList list;
        std::vector<TempDevice> devices;

        friend void to_json(json &j, const Dev &d)
        {
            json temp;
            to_json(temp, d.list);
            j["tmp_list"] = temp;

            for (const auto &item : d.devices) {
                json t;
                to_json(t, item);
                j["tmp_devices"].push_back(t);
            }
        }
    };

    Dev dev;

    AutoFindAck_t(const std::string &s, const std::string &sn, const std::string &m, const HardWare_t &h, uint8_t addr)
    {
        tick = 1;
        time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
        src = s;
        ack.tick = 1;
        ack.time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();

        dev.list.count = 1;
        TempDevice device(sn, m, h, addr);
        dev.devices.emplace_back(device);
    }

    friend void to_json(json &j, const AutoFindAck_t &a)
    {
        j["tick"] = a.tick;
        j["time"] = a.time;
        j["src"] = a.src;
        j["ack"]["tick"] = a.ack.tick;
        j["ack"]["time"] = a.ack.time;

        json temp;
        to_json(temp, a.dev);
        j["dev"] = temp;
    }
};

namespace Upgrade {
    struct DeviceUpgrade_t : Msg_t {
        struct Point {
            std::string key;
            std::string val;

            friend void from_json(const json &j, Point &p)
            {
                if (j.contains("key")) {
                    j.at("key").get_to(p.key);
                }

                if (j.contains("val")) {
                    j.at("val").get_to(p.val);
                }
            }

            friend void to_json(json &j, const Point &p)
            {
                j["key"] = p.key;
                j["val"] = p.val;
            }
        };

        struct Device {
            std::size_t id;
            std::vector<Point> points;

            friend void from_json(const json &j, Device &d)
            {
                if (j.contains("id")) {
                    j.at("id").get_to(d.id);
                }

                Point point {"status", "failed"};
                d.points.emplace_back(point);

                if (j.contains("points")) {
                    if (j.at("points").is_array()) {
                        for (const auto &item : j.at("points")) {
                            Point p;
                            item.get_to(p);
                            d.points.emplace_back(p);
                        }
                    }
                }
            }

            friend void to_json(json &j, const Device &d)
            {
                j["id"] = d.id;

                for (const auto &p : d.points) {
                    json temp;
                    to_json(temp, p);
                    j["points"].push_back(temp);
                }
            }
        };

        struct Dev {
            std::vector<Device> devices;

            friend void from_json(const json &j, Dev &d)
            {
                if (j.contains("devices")) {
                    if (j.at("devices").is_array()) {
                        for (const auto &item : j.at("devices")) {
                            Device dev;
                            item.get_to(dev);
                            d.devices.emplace_back(dev);
                        }
                    }
                }
            }

            friend void to_json(json &j, const Dev &d)
            {
                for (const auto &dev : d.devices) {
                    json temp;
                    to_json(temp, dev);
                    j["devices"].push_back(temp);
                }
            }
        };

        Dev _dev;

        std::size_t getId(void)
        {
            if (_dev.devices.empty()) {
                return 0;
            }

            return _dev.devices.front().id;
        }

        std::string getPath(void)
        {
            if (_dev.devices.empty()) {
                return "";
            }

            if (_dev.devices.front().points.empty()) {
                return "";
            }

            for (const auto &[k, v] : _dev.devices.front().points) {
                if (k == "path") {
                    return v;
                }
            }

            return "";
        }

        void setSrc(const std::string_view &src)
        {
            this->src = src;
        }

        void setStatus(bool stat)
        {
            if (_dev.devices.empty()) {
                return;
            }

            if (_dev.devices.front().points.empty()) {
                return;
            }

            for (auto &[k, v] : _dev.devices.front().points) {
                if (k == "status") {
                    v = stat ? "success" : "failed";
                    break;
                }
            }
        }

        friend void from_json(const json &j, DeviceUpgrade_t &d)
        {
            if (j.contains("src")) {
                j.at("src").get_to(d.src);
            }

            if (j.contains("tick")) {
                j.at("tick").get_to(d.tick);
            }

            if (j.contains("time")) {
                j.at("time").get_to(d.time);
            }

            if (j.contains("dev")) {
                j.at("dev").get_to(d._dev);
            }
        }

        friend void to_json(json &j, const DeviceUpgrade_t &d)
        {
            j["tick"] = d.tick;
            j["time"] = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
            j["src"] = d.src;

            json temp;
            to_json(temp, d._dev);

            j["dev"] = temp;
        }
    };

    using DeviceUpgradeAck_t = DeviceUpgrade_t;
}

};
